using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.Data.Schema.Extensibility;
using Microsoft.Data.Schema.SchemaModel;
using Microsoft.Data.Schema.ScriptDom.Sql;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.Data.Schema.Sql;
using Microsoft.Data.Schema.StaticCodeAnalysis;
using System.Text.RegularExpressions;

using System.Linq;

namespace Neznayka
{
    /// <summary>
    /// This is a SQL rule which returns a warning message 
    /// whenever there is a create table #name statement in online subroutine. 
    /// This rule only applies to SQL stored procedures.
    /// </summary>

    [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
    [DataRuleAttribute(
        NeznaykaConstants.NameSpace,
        NeznaykaConstants.EnforceNamedConstraintRuleId,
        NeznaykaConstants.ResourceBaseName,
        NeznaykaConstants.EnforceNamedConstraint_RuleName,
        NeznaykaConstants.CategoryDatabaseStructures,
        DescriptionResourceId = NeznaykaConstants.EnforceNamedConstraint_ProblemDescription)]
    [SupportedElementType(typeof(ISqlProcedure))]
    [SupportedElementType(typeof(ISqlTrigger))]
    [SupportedElementType(typeof(ISqlFunction))]
    [SupportedElementType(typeof(ISqlView))]
    [SupportedElementType(typeof(ISqlTable))]
    public class EnforceNamedConstraintRule : StaticCodeAnalysisRule
    {
        #region Overrides
        /// <summary>
        /// Analyze the model element
        /// </summary>
        public override IList<DataRuleProblem> Analyze(DataRuleSetting ruleSetting, DataRuleExecutionContext context)
        {
            // (Re)-Load Environment settings
            // (Re)-Load Environment settings
            List<DataRuleProblem> problems;
            SqlSchemaModel sqlSchemaModel;
            ISqlModelElement sqlElement;
            TSqlFragment sqlFragment;
            DMVRuleSetup.RuleSetup(context, out problems, out sqlSchemaModel, out sqlElement, out sqlFragment);

            // visitor to get the occurrences of constraints we want to be named
            EnforceNamedConstraintVisitor enforceNamedConstraintVisitor = new EnforceNamedConstraintVisitor();
            sqlFragment.Accept(enforceNamedConstraintVisitor);
            List<Constraint> constraints = enforceNamedConstraintVisitor.Constraints;

            // visitor to get the occurrences of table variable declarations we are not interested in 
            EnforceNamedConstraintDeclareTableVisitor enforceNamedConstraintDeclareTableVisitor = new EnforceNamedConstraintDeclareTableVisitor();
            sqlFragment.Accept(enforceNamedConstraintDeclareTableVisitor);
            List<TSqlFragment> tableDeclarations = enforceNamedConstraintDeclareTableVisitor.Objects;

            // every unnamed constraint ( outside of table declaration ) is a problem.
            List<Constraint> issues = constraints.Where(cons => !tableDeclarations.Any(dec => SqlComparisonUtils.Contains(dec, cons))).Select(n => n).ToList();

            // Create problems for each constraint wo a name
            foreach (TSqlFragment issue in issues)
            {
                DataRuleProblem problem = new DataRuleProblem(this,
                                            String.Format(CultureInfo.CurrentCulture, this.RuleProperties.Description, SqlRuleUtils.GetElementName(sqlSchemaModel, sqlElement)),
                                            sqlElement);

                SqlRuleUtils.UpdateProblemPosition(problem, issue.StartOffset, issue.FragmentLength);
                problems.Add(problem);
            }


            return problems;
        }

        #endregion    
 
    }
}
